home *** CD-ROM | disk | FTP | other *** search
/ PCMania 64 / PCMania CD64_1.iso / phy / phy003 / files / articulo.t07 < prev    next >
Encoding:
Text File  |  1997-04-11  |  8.6 KB  |  180 lines

  1. ε                Curso de programación orientada a objetos (III)π
  2.  
  3. µ     Teoria y uso de los streamsπ
  4.  
  5.   Otra de esas nuevas cositas "filosóficas" que nos proporciona la POO es el
  6. envio de mensajes a los objetos (de esto ya hemos hablado), y la pantalla,
  7. como el disco u otros sistemas de comunicación, son objetos y como tales se
  8. define una nueva forma de interactuación. Frente a los betustos y a veces
  9. frustrantes 'δprintf()π' y 'δfread()π' se define ahora 'δcinπ' i 'δcoutπ' (dentro de
  10. la libreria 'Ωiostream.hπ') que aceptan cualquier tipo de datos predefinidos y
  11. cualquiera que le podamos definir en un futuro (esto es una cosa fantástica
  12. que veremos más adelante).
  13.  
  14.   Antes, con 'δprintfπ', por ejemplo, teniamos que decir de que tipo de datos
  15. se trataba lo que queriamos escribir:
  16. Γ     printf("Mi casa es de color %s", color);π
  17. Ahora eso ya no hace falta, simplemente llamamos para escribir lo que haya
  18. que escribir que ya se las arreglará el compilador para saber de que tipo
  19. son los datos. La sintaxis de C++ para escribir en streams (que así se llaman
  20. en inglés los ficheros, entre los que se encuentra la pantalla (aquí todo son
  21. ficheros, como en UNIX!! :) se podria ejemplificar así:
  22. Γ     cout << "Mi casa es de color " << color;π
  23.  
  24.   Por supuesto, se pueden meter más signos δ<<π para seguir escribiendo más cosas
  25. e incluso para hacer retornos de carros y todo lo que se podia hacer con el C
  26. tradicional. Al igual que el objeto 'δcoutπ' redirige a la salida estandar, el
  27. antes mencionado 'δcinπ' hace lo própio con la entrada estandar. Además en otro
  28. número veremos como todo esto es tambien aplicable a los ficheros de nuestro
  29. disco duro haciendonos la vida más facil.
  30.  
  31. µ     Métodos inlineπ
  32.  
  33.   Los métodos inline son el sustituto natural de las macros de C. Han de
  34. definirse e implementarse obligatoriamente detro de la própia definición de la
  35. clase y no pueden ser estáticos (ya veremos que es ésto). Un ejemplo podria
  36. ser el siguiente:
  37.  
  38. Γ class Inlines {π
  39. Γ   private:     π
  40. Γ     int status;π
  41. Γ     [...]      π
  42. Γ   public:      π
  43. Γ     [...]      π
  44. Γ     inline int error(void) { return status; }π
  45. Γ     [...]π
  46. Γ  };π
  47.  
  48.   Observa que la definición y el cuerpo del método van unidos y no hay que
  49. redefinirlo en ningún otro sítio. Esta función de ejemplo, lo único que hace
  50. es devolver un valor de una variable privada que supuestamente controla los
  51. errores de la clase; por ello podria resultar un poco lento implementarlo como
  52. un método normal (porque habria que añadir una llamada a procedimiento y demás).
  53.  
  54. µ     Constructores y destructoresπ
  55.  
  56.   Muchas veces, si programas en Pascal usando TPUs, habrás usado la parte de
  57. la TPU que se ejecuta siempre que se incluye ésta, para inicializar variables
  58. o mostrar mensajes, pero tambien otras veces te hubiera podido hacer falta
  59. hacer lo mismo para acabar y no lo has podido hacer. Los Ωconstructoresπ de C++
  60. son unos métodos que se ejecutan cada vez que creas una instancia de un objeto
  61. y que te permiten inicializar variables, pedir memoria, etc. Los Ωdestructoresπ
  62. son un método que tiene cada clase y que se ejecuta al eliminarse cada objeto
  63. instanciado.
  64.   Para ver la potencia de los constructores y destructores podemos imaginar
  65. que queremos crear una clase que maneje una lista de números complejos u otros
  66. objetos de forma dinámica (pidiendo memoria cada vez que se añade un elemento
  67. y liberandola cada vez que se elimina uno). Si usaramos los métodos
  68. tradicionales, tendriamos que pedir memoria explícitamente en cada método,
  69. usando el paradigma de la OO podemos crear un constructor que cada vez que se
  70. instancie un objeto, reserve la memoria suficiente para contenerse y luego
  71. automáticamente se libere esa memoria.
  72.   La definición de los constructores es bastante sencilla: se trata de un
  73. método que no devuelve nada y que tiene el mismo nombre que la clase que lo
  74. contiene. De forma similar, el destructor es un método que no devuelve nada y 
  75. tampoco recibe parámetros de entrada y cuyo nombre es el mismo de la clase,
  76. pero precedido del signo tilde δ~π (ALT+126). Un ejemplo:
  77.  
  78. Γ class Coche {π
  79. Γ     [...]    π
  80. Γ   public:    π
  81. Γ     [...]    π
  82. Γ       Coche(char *Marca, char *Modelo);π
  83. Γ      ~Coche();                         π
  84. Γ  };          π
  85.  
  86. Γ Coche :: Coche(char *Marca, char *Modelo) {π
  87. Γ   [...]                                    π
  88. Γ   }                                        π
  89.  
  90. Γ Coche :: ~Coche() {  π
  91. Γ   [...]              π
  92. Γ   }                  π
  93.  
  94.   Podemos observar que la definición debe estar en la parte pública ya que si
  95. no fuese así, el compilador no podria acceder a los métodos Coche y ~Coche.
  96. Hay que observar tambien, que el destructor ∞NUNCAπ puede llevar parámetros,
  97. mientras el constructor puede llevarlos o no.
  98.  
  99. µ     La herenciaπ
  100.  
  101.   Como parábola de la realidad, la heréncia es una forma de que los que vienen
  102. después cojan lo que se ha creado por/para los que habia antes. Imaginemos que
  103. tenemos una clase "δAvionπ" en la que están definidas una série de abstracciones
  104. generales de todos los aviones (combustible, altura máxima, peso, etc). Ahora
  105. queremos hacer una clase "δBombarderoπ" y nos encontramos con el problema de que
  106. tenemos que reescribir todos los métodos anteriores junto con las variables y
  107. demás. ¿Como solucionar ésto? Con la herencia. Si de alguna manera pudiesemos
  108. aprovechar el trabajo realizado con "δAvionπ" y no tenerlo que reescribir para
  109. "δBombarderoπ" estariamos ganando mucho tiempo y trabajo. Si hacemos que la
  110. clase "δBombarderoπ" φHEREDEπ de "δAvionπ", solucionamos el problema; podriamos
  111. hacerlo así:
  112.  
  113. Γ class Avion {π
  114. Γ   private:   π
  115. Γ     int combustible;π
  116. Γ     int velocidad;  π
  117. Γ   public:           π
  118. Γ     void acelera(int v);π
  119. Γ          Avion();       π
  120. Γ         ~Avion();       π
  121. Γ  };                     π
  122.  
  123. Γ class Bombardero : public Avion { π
  124. Γ   private:                        π
  125. Γ     int num_bombas;               π
  126. Γ   public:                         π
  127. Γ     inline int lanza(void) { return --num_bombas; }π
  128. Γ          Bombardero();                             π
  129. Γ         ~Bombardero();                             π
  130. Γ  };                                                π
  131.  
  132.   La primera clase ("δAvionπ") no tiene nada nuevo, pero la clase "δBombarderoπ"
  133. ya va teniendo en la primera línea cosas "extrañas". Con ese tipo de definición,
  134. lo que queremos decirle al compilador es: "Ωdefineme una clase llamada π
  135. ΩBombardero que herede públicamente (lo que era publico que lo siga siendo) deπ
  136. Ωla clase Avion todos sus atributosπ". Así, en la clase "δBombarderoπ", no tendremos
  137. como privada sólo 1 variable ("δnum_bombasπ"), sino que tendremos 3, las 2 de
  138. "δAvionπ" y la própia. De la misma forma, no tendremos como pública sólo el
  139. método "δlanza()π" y el constructor+destructor, sino que heredamos el método
  140. "δacelera()π" de la clase padre.
  141.  
  142. µ     Uso de directivas del compiladorπ
  143.  
  144.   No sólo para la POO es necesario usar bien las directivas que te ofrece el
  145. compilador, es una forma de hacer más robusto y legible nuestro código. Las
  146. que se usan principalmente suelen ser δ#ifdefπ, δ#ifndefπ, δ#endifπ e δ#includeπ.
  147. No hay mucho que decir acerca de todas ellas, pues su uso está claro, pero es
  148. muy interesante que comienzes a meterlas por tus clases para que el compilador
  149. no te de grandes errores. Un uso frequente y que recomiendo es el siguiente:
  150.  
  151. Γ  // Fichero "claseA.hpp"π
  152.  
  153. Γ  #ifndef __CLASE_A__    π
  154. Γ  #define __CLASE_A__    π
  155.  
  156. Γ  class A {              π
  157. Γ    [...]                π
  158. Γ   };                    π
  159.  
  160. Γ  #endif                 π
  161.  
  162. µ     Reglas prácticas para compilarπ
  163.  
  164.   Si estas muy habituado a usar C sin crearte ficheros de proyecto, puede que
  165. caigas muchas veces (hasta que te acostumbres) en el error de incluir tanto la
  166. definición como la implementación de cada una de las clases usadas. Si lo haces
  167. así e incluyes una misma clase en 2 lugares distintos, el compilador te dará
  168. un error de duplicación en la definición.
  169.   Para evitar todos los errores de este tipo, lo mejor es tener claro donde
  170. debes poner las directivas del compilador y usar los ficheros de proyecto.
  171. Para usarlos sólo tienes que crear uno con el entorno que te proporciona el
  172. compilador e insertar allí todos los ficheros auxiliares que uses. Además,
  173. sólo deberás incluir los ficheros de definición de cada clase usada y nunca
  174. la implementación de éstas.
  175.   Ya hay suficiente por éste número, en el próximo intentaremos ver nuestros
  176. primeros programas compilables, pero hasta entonces es muy recomendable que
  177. vayas investigando por tu cuenta (hay muchas clases interesantes en los
  178. compiladores comerciales). Hasta la vista.
  179.  
  180.